#!/bin/sh

########################################################################
# This test checks that the core handles loading and unloading or several
# targets correctly.
# 
# Usage: 
#   sh test_basics.sh
########################################################################

# Just in case the tools like lsmod are not in their usual location.
export PATH=$PATH:/sbin:/bin:/usr/bin

########################################################################
# A function to check prerequisites: whether the necessary files exist,
# etc.
########################################################################
checkPrereqs()
{
	if test ! -f "${CORE_MODULE}"; then
		printf "The core module is missing: ${CORE_MODULE}\n"
		exit 1
	fi

	if test ! -f "${REPORTER_MODULE}"; then
		printf "The \"trace reporter\" module is missing: ${REPORTER_MODULE}\n"
		exit 1
	fi
		
	if test ! -f "${TARGET_MODULE_KEDR}"; then
		printf "The target module is missing: ${TARGET_MODULE_KEDR}\n"
		exit 1
	fi

	if test ! -f "${TARGET_MODULE_TEST}"; then
		printf "The target module is missing: ${TARGET_MODULE_TEST}\n"
		exit 1
	fi

	if test ! -f "${TARGET_MODULE_A}"; then
		printf "The target module is missing: ${TARGET_MODULE_A}\n"
		exit 1
	fi

	if test ! -f "${TARGET_MODULE_B}"; then
		printf "The target module is missing: ${TARGET_MODULE_B}\n"
		exit 1
	fi
}

########################################################################
cleanupTargets()
{
	printf "Cleaning up the targets...\n"

	lsmod | grep "${TARGET_NAME_KEDR}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${TARGET_NAME_KEDR}"
	fi

	lsmod | grep "${TARGET_NAME_TEST}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${TARGET_NAME_TEST}"
	fi

	lsmod | grep "${TARGET_NAME_A}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${TARGET_NAME_A}"
	fi

	lsmod | grep "${TARGET_NAME_B}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${TARGET_NAME_B}"
	fi
}

########################################################################
# Cleanup function
########################################################################
cleanupAll()
{
	cd "${WORK_DIR}"

	cleanupTargets
	
	lsmod | grep "${REPORTER_MODULE_NAME}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${REPORTER_MODULE_NAME}"
	fi
	
	lsmod | grep "${CORE_MODULE_NAME}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${CORE_MODULE_NAME}"
	fi

	umount "${TEST_DEBUGFS_DIR}"
}

########################################################################
loadReporter()
{
	insmod "${REPORTER_MODULE}" \
		report_calls=0 report_mem=0 report_load=1
	if test $? -ne 0; then
		printf "Failed to load the module: ${REPORTER_MODULE_NAME}\n"
		cleanupAll
		exit 1
	fi
}

unloadReporter()
{
	rmmod "${REPORTER_MODULE_NAME}"
	if test $? -ne 0; then
		printf "Failed to unload the module: ${REPORTER_MODULE_NAME}\n"
		cleanupAll
		exit 1
	fi
}

########################################################################
loadTargets()
{
	insmod "${TARGET_MODULE_KEDR}"
	if test $? -ne 0; then
		printf "Failed to load the target module: ${TARGET_MODULE_KEDR}\n"
		cleanupAll
		exit 1
	fi

	insmod "${TARGET_MODULE_TEST}"
	if test $? -ne 0; then
		printf "Failed to load the target module: ${TARGET_MODULE_TEST}\n"
		cleanupAll
		exit 1
	fi

	insmod "${TARGET_MODULE_A}"
	if test $? -ne 0; then
		printf "Failed to load the target module: ${TARGET_MODULE_A}\n"
		cleanupAll
		exit 1
	fi

	insmod "${TARGET_MODULE_B}"
	if test $? -ne 0; then
		printf "Failed to load the target module: ${TARGET_MODULE_B}\n"
		cleanupAll
		exit 1
	fi
}

unloadTargets()
{
	lsmod | grep "${TARGET_NAME_KEDR}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${TARGET_NAME_KEDR}"
		if test $? -ne 0; then
			printf "Failed to unload the module: ${TARGET_NAME_KEDR}\n"
			cleanupAll
			exit 1
		fi

	fi

	lsmod | grep "${TARGET_NAME_TEST}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${TARGET_NAME_TEST}"
		if test $? -ne 0; then
			printf "Failed to unload the module: ${TARGET_NAME_TEST}\n"
			cleanupAll
			exit 1
		fi
	fi

	lsmod | grep "${TARGET_NAME_A}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${TARGET_NAME_A}"
		if test $? -ne 0; then
			printf "Failed to unload the module: ${TARGET_NAME_A}\n"
			cleanupAll
			exit 1
		fi
	fi

	lsmod | grep "${TARGET_NAME_B}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${TARGET_NAME_B}"
		if test $? -ne 0; then
			printf "Failed to unload the module: ${TARGET_NAME_B}\n"
			cleanupAll
			exit 1
		fi
	fi
}

########################################################################
checkLoadedTargets()
{
	printf "Loaded targets: \n"
	cat "${TEST_DEBUGFS_TARGETS_FILE}" | sort | sed -e 's/^/    /'

	cat "${TEST_DEBUGFS_TARGETS_FILE}" | sort | tr "\n" " " | sed -e 's/ $//' > "${TEST_LOADED_FILE}"
	if test $? -ne 0; then
		printf "Failed to read the list of loaded targets from "
		printf "${TEST_DEBUGFS_TARGETS_FILE}\n"
		cleanupAll
		exit 1
	fi

	target_str=$(cat "${TEST_LOADED_FILE}")
	if test "t${target_str}" != "t$1"; then
		printf "The list of targets differs from the expected one (\"$1\")\n"
		cleanupAll
		exit 1
	fi
}

########################################################################
# doTestExplicit() - check the core loaded with the explicit list of
# targets
########################################################################
doTestExplicit()
{
	printf "Starting doTestExplicit()\n"

	insmod "${CORE_MODULE}" \
		targets="${TARGET_NAME_B},${TARGET_NAME_TEST};${TARGET_NAME_KEDR}"
	if test $? -ne 0; then
		printf "Failed to load the core module: ${CORE_MODULE}\n"
		cleanupAll
		exit 1
	fi

	loadReporter

	printf "Checkpoint 01\n"
	checkLoadedTargets "none"

	loadTargets

	printf "Checkpoint 02\n"
	checkLoadedTargets "kedr_multi_a some_multi_b test_multi_a"

	rmmod "${TARGET_NAME_KEDR}"
	if test $? -ne 0; then
		printf "Failed to unload the module: ${TARGET_NAME_KEDR}\n"
		cleanupAll
		exit 1
	fi

	printf "Checkpoint 03\n"
	checkLoadedTargets "some_multi_b test_multi_a"

	rmmod "${TARGET_NAME_TEST}"
	if test $? -ne 0; then
		printf "Failed to unload the module: ${TARGET_NAME_TEST}\n"
		cleanupAll
		exit 1
	fi

	printf "Checkpoint 04\n"
	checkLoadedTargets "some_multi_b"

	insmod "${TARGET_MODULE_KEDR}"
	if test $? -ne 0; then
		printf "Failed to load the target module: ${TARGET_MODULE_KEDR}\n"
		cleanupAll
		exit 1
	fi

	printf "Checkpoint 05\n"
	checkLoadedTargets "kedr_multi_a some_multi_b"

	unloadTargets

	printf "Checkpoint 06\n"
	checkLoadedTargets "none"
	
	# Get the trace.
	TEST_TRACE_FILE="${TEST_TMP_DIR}/trace_test_explicit.txt"
	cat "${TEST_DEBUGFS_OUTPUT_FILE}" > "${TEST_TRACE_FILE}"
	if test $? -ne 0; then
		printf "Failed to read data from the output file in debugfs: "
		printf "${TEST_DEBUGFS_OUTPUT_FILE}\n"
		cleanupAll
		exit 1
	fi
	
	# Unload the remaining modules, they are no longer needed
	unloadReporter	
	rmmod "${CORE_MODULE_NAME}"
	if test $? -ne 0; then
		printf "Failed to unload the core module.\n"
		cleanupAll
		exit 1
	fi
	
	cat "${TEST_TRACE_FILE}" | tr "\n" " " | grep -E "${MATCH_EXPR_EXPLICIT}" > /dev/null
	if test $? -ne 0; then
		printf "No matching substring has been found in the trace for the "
		printf "expression \"${MATCH_EXPR_EXPLICIT}\".\n"
		cleanupAll
		exit 1
	fi 
}

########################################################################
# doTestAny() - check the core loaded with '*' as the list of targets or
# without 'targets' parameter set.
########################################################################
doTestAny()
{
	printf "Starting doTestAny()\n"

	target_spec=""
	if test "t$1" != "tdefault"; then
		target_spec="targets=\"*\""
	fi

	insmod "${CORE_MODULE}" ${target_spec}
	if test $? -ne 0; then
		printf "Failed to unload the core module: ${CORE_MODULE}\n"
		cleanupAll
		exit 1
	fi

	loadReporter

	printf "Checkpoint 01\n"
	checkLoadedTargets "none"

	loadTargets

	printf "Checkpoint 02\n"
	checkLoadedTargets "some_multi_a some_multi_b"

	rmmod "${TARGET_NAME_B}"
	if test $? -ne 0; then
		printf "Failed to unload the module: ${TARGET_NAME_B}\n"
		cleanupAll
		exit 1
	fi

	printf "Checkpoint 03\n"
	checkLoadedTargets "some_multi_a"

	rmmod "${TARGET_NAME_TEST}"
	if test $? -ne 0; then
		printf "Failed to unload the module: ${TARGET_NAME_TEST}\n"
		cleanupAll
		exit 1
	fi

	printf "Checkpoint 04\n"
	checkLoadedTargets "some_multi_a"

	insmod "${TARGET_MODULE_B}"
	if test $? -ne 0; then
		printf "Failed to load the target module: ${TARGET_MODULE_B}\n"
		cleanupAll
		exit 1
	fi

	printf "Checkpoint 05\n"
	checkLoadedTargets "some_multi_a some_multi_b"

	unloadTargets

	printf "Checkpoint 06\n"
	checkLoadedTargets "none"

	# Get the trace.
	TEST_TRACE_FILE="${TEST_TMP_DIR}/trace_test_any.txt"
	cat "${TEST_DEBUGFS_OUTPUT_FILE}" > "${TEST_TRACE_FILE}"
	if test $? -ne 0; then
		printf "Failed to read data from the output file in debugfs: "
		printf "${TEST_DEBUGFS_OUTPUT_FILE}\n"
		cleanupAll
		exit 1
	fi

	# Unload the remaining modules, they are no longer needed
	unloadReporter
	rmmod "${CORE_MODULE_NAME}"
	if test $? -ne 0; then
		printf "Failed to unload the core module.\n"
		cleanupAll
		exit 1
	fi

	cat "${TEST_TRACE_FILE}" | tr "\n" " " | grep -E "${MATCH_EXPR_ANY}" > /dev/null
	if test $? -ne 0; then
		printf "No matching substring has been found in the trace for the "
		printf "expression \"${MATCH_EXPR_ANY}\".\n"
		cleanupAll
		exit 1
	fi 
}

########################################################################
# main
########################################################################
WORK_DIR=${PWD}

if test $# -ne 0; then
	printf "Usage: sh $0\n"
	exit 1
fi

CORE_MODULE_NAME="@CORE_MODULE_NAME@"
CORE_MODULE="@CORE_MODULE_DIR@/${CORE_MODULE_NAME}.ko"

REPORTER_MODULE_NAME="kedr_test_reporter"
REPORTER_MODULE="@CMAKE_BINARY_DIR@/core/tests/reporter/${REPORTER_MODULE_NAME}.ko"

TARGET_NAME_KEDR="kedr_multi_a"
TARGET_MODULE_KEDR="@CMAKE_CURRENT_BINARY_DIR@/${TARGET_NAME_KEDR}/${TARGET_NAME_KEDR}.ko"

TARGET_NAME_TEST="test_multi_a"
TARGET_MODULE_TEST="@CMAKE_CURRENT_BINARY_DIR@/${TARGET_NAME_TEST}/${TARGET_NAME_TEST}.ko"

TARGET_NAME_A="some_multi_a"
TARGET_MODULE_A="@CMAKE_CURRENT_BINARY_DIR@/${TARGET_NAME_A}/${TARGET_NAME_A}.ko"

TARGET_NAME_B="some_multi_b"
TARGET_MODULE_B="@CMAKE_CURRENT_BINARY_DIR@/${TARGET_NAME_B}/some-multi_b.ko"

TEST_TMP_DIR="@KEDR_TEST_TEMP_DIR@/basics"
TEST_DEBUGFS_DIR="${TEST_TMP_DIR}/debug"
TEST_DEBUGFS_OUTPUT_FILE="${TEST_DEBUGFS_DIR}/kedr_test_reporter/output"
TEST_DEBUGFS_TARGETS_FILE="${TEST_DEBUGFS_DIR}/kedr_mem_core/loaded_targets"
TEST_LOADED_FILE="${TEST_TMP_DIR}/loaded.txt"

MATCH_EXPR_EXPLICIT="TARGET LOAD name=\"kedr_multi_a\".*TARGET LOAD name=\"test_multi_a\".*TARGET LOAD name=\"some_multi_b\".*TARGET UNLOAD name=\"kedr_multi_a\".*TARGET UNLOAD name=\"test_multi_a\".*TARGET LOAD name=\"kedr_multi_a\".*TARGET UNLOAD name=\"kedr_multi_a\".*TARGET UNLOAD name=\"some_multi_b\".*"

MATCH_EXPR_ANY="TARGET LOAD name=\"some_multi_a\".*TARGET LOAD name=\"some_multi_b\".*TARGET UNLOAD name=\"some_multi_b\".*TARGET UNLOAD name=\"some_multi_a\".*"

rm -rf "${TEST_TMP_DIR}"
mkdir -p "${TEST_DEBUGFS_DIR}"
if test $? -ne 0; then
	printf "Failed to create ${TEST_DEBUGFS_DIR}\n"
	exit 1
fi

checkPrereqs

mount -t debugfs none "${TEST_DEBUGFS_DIR}"
if test $? -ne 0; then
	printf "Failed to mount debugfs to ${TEST_DEBUGFS_DIR}\n"
	cleanupAll
	exit 1
fi

doTestExplicit
doTestAny
doTestAny default

cleanupAll

# test passed
exit 0
